package net.cyue.nest.nestjava.core.injector;

import net.cyue.nest.nestjava.common.annotation.core.Inject;
import net.cyue.nest.nestjava.common.annotation.core.Injectable;
import net.cyue.nest.nestjava.common.data.InjectableMetaType;
import net.cyue.nest.nestjava.common.util.ReflectUtil;

import java.lang.reflect.Field;

public class Injector
{
  public static DefaultInstanceRegistry INSTANCE_REGISTRY = new DefaultInstanceRegistry();

  /**
   * @param inject 注解
   * @return name 或者 null
   */
  public static String getInjectName(Inject inject) {
    if (inject == null) {
      throw new NullPointerException("inject is null");
    }
    String name = inject.value();
    if (name == null || name.isEmpty() || name.isBlank()) {
      name = inject.name();
    }
    if (name == null || name.isEmpty() || name.isBlank()) {
      return null;
    }
    return name;
  }
  /**
   * @param injectable 注解
   * @return name 或者 null
   */
  public static String getInjectableName(Injectable injectable) {
    if (injectable == null) {
      throw new NullPointerException("injectable is null");
    }
    String name = injectable.value();
    if (name == null || name.isEmpty() || name.isBlank()) {
      name = injectable.name();
    }
    if (name == null || name.isEmpty() || name.isBlank()) {
      return null;
    }
    return name;
  }

  public static Object getInjectableInstance(Inject inject) {
    if (inject == null) {
      throw new NullPointerException("inject is null");
    }
    String name = Injector.getInjectName(inject);
    if (name == null) {
      return null;
    }
    // 影响 InjectableMetaType 的 hashCode 的只有 name
    InjectableMetaType mt = new InjectableMetaType((Class<?>) null, name);
    return Injector.INSTANCE_REGISTRY.getInstance(mt);
  }

  public static void doInject(Object obj, Field field) {
    Class<?> clz = field.getType();
    Inject inject = field.getAnnotation(Inject.class);
    if (inject == null) {
      Object instance = Injector.INSTANCE_REGISTRY.getInstance(clz);
      ReflectUtil.setFieldValue(obj, field, instance);
      return;
    }
    String name = Injector.getInjectName(inject);
    if (name == null) {
      Object instance = Injector.INSTANCE_REGISTRY.getInstance(clz);
      ReflectUtil.setFieldValue(obj, field, instance);
    }
    InjectableMetaType iMT = new InjectableMetaType(clz, name);
    Object instance = Injector.INSTANCE_REGISTRY.getInstance(iMT);
    ReflectUtil.setFieldValue(obj, field, instance);
  }
  public static void doInject(Object obj) {
    if (obj == null) {
      throw new NullPointerException();
    }
    Field[] fields = obj.getClass().getDeclaredFields();
    for(Field f : fields) {
      try {
        if (f.get(obj) == null) {
          f.setAccessible(true);
          Injector.doInject(obj, f);
        }
      } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
      }
    }
  }

}

